package com.atguigu.gmall.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.cart.client.CartFeignClient;
import com.atguigu.gmall.common.RabbitService;
import com.atguigu.gmall.common.util.HttpClientUtil;
import com.atguigu.gmall.constant.MqConst;
import com.atguigu.gmall.model.cart.CartInfo;
import com.atguigu.gmall.model.enums.OrderStatus;
import com.atguigu.gmall.model.enums.ProcessStatus;
import com.atguigu.gmall.model.order.OrderDetail;
import com.atguigu.gmall.model.order.OrderInfo;
import com.atguigu.gmall.order.mapper.OrderDetailMapper;
import com.atguigu.gmall.order.mapper.OrderInfoMapper;
import com.atguigu.gmall.order.service.OrderService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Service
public class OrderServiceImpl extends ServiceImpl<OrderInfoMapper,OrderInfo> implements OrderService {

    @Autowired
    private CartFeignClient cartFeignClient;

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Value("${ware.url}")
    private String wareUrl; // http://localhost:9001

    @Autowired
    private RabbitService rabbitService;

    @Override
    public Map<String, Object> getTrade(String userId) {
        //  声明一个map 集合
        Map<String,Object> map = new HashMap<>();
        //  获取送货清单列表.
        List<CartInfo> cartCheckedList = cartFeignClient.getCartCheckedList(userId);
        //  保证这个对象具有原子性 int i = 0; i++;不具备原子性
        //  AtomicInteger 底层用的啥技术：CAS; 比较并交换
        AtomicInteger totalNum = new AtomicInteger();
        //        AtomicReference<BigDecimal> totalAmount = new AtomicReference<>(new BigDecimal("0"));
        List<OrderDetail> orderDetailList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(cartCheckedList)){
            //  循环遍历赋值，给orderDetail ;并间隔这个orderDetail 放入集合.
            orderDetailList = cartCheckedList.stream().map(cartInfo -> {
                OrderDetail orderDetail = new OrderDetail();
                orderDetail.setSkuId(cartInfo.getSkuId());
                orderDetail.setSkuName(cartInfo.getSkuName());
                orderDetail.setImgUrl(cartInfo.getImgUrl());
                orderDetail.setOrderPrice(cartInfo.getSkuPrice());
                orderDetail.setSkuNum(cartInfo.getSkuNum());
                //  计算总件数；
                totalNum.addAndGet(cartInfo.getSkuNum());
                //                totalAmount.updateAndGet(v -> v + orderDetail.getOrderPrice().multiply(new BigDecimal(orderDetail.getSkuNum())));
                //  返回数据.
                return orderDetail;
            }).collect(Collectors.toList());
            //  存储数据.
            map.put("detailArrayList",orderDetailList);
        }
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setOrderDetailList(orderDetailList);
        //  计算总金额  将计算结果赋值给当前属性private BigDecimal totalAmount;
        orderInfo.sumTotalAmount();
        //  存储总件数：
        map.put("totalNum",totalNum);
        //  总价：
        map.put("totalAmount",orderInfo.getTotalAmount());
        //  将流水号传递到页面同时保存到redis.
        map.put("tradeNo",this.getTradeNo(userId));
        //  返回map
        return map;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long saveOrderInfo(OrderInfo orderInfo) {
        //  orderInfo; 保存完成之后就能获取到订单Id
        //  没有处理的字段 total_amount order_status user_id out_trade_no trade_body operate_time expire_time process_status
        //  单价*数据;  orderDetailList 这个字段通过映射 就能获取到前端传递的数据!
        //  Json 对象中的属性与实体类的属性名一致 orderDetailList
        orderInfo.sumTotalAmount();
        orderInfo.setOrderStatus(OrderStatus.UNPAID.name());
        //  out_trade_no 必须要唯一，不能重复.
        String outTradeNo = "ATGUIGU"+System.currentTimeMillis()+new Random().nextInt(1000);
        orderInfo.setOutTradeNo(outTradeNo);
        //  订单秒杀
        orderInfo.setTradeBody("硅谷商店");
        orderInfo.setOperateTime(new Date());
        //  过期时间:24h = 1d
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE,1);
        orderInfo.setExpireTime(calendar.getTime());
        orderInfo.setProcessStatus(ProcessStatus.UNPAID.name());

        this.orderInfoMapper.insert(orderInfo);

        //  获取订单Id
        Long orderId = orderInfo.getId();
        //  获取订单明细数据
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        if (!CollectionUtils.isEmpty(orderDetailList)){
            orderDetailList.forEach(orderDetail -> {
                //  赋值 orderId
                orderDetail.setOrderId(orderId);
                orderDetailMapper.insert(orderDetail);
            });
        }
        //  发送延迟消息.
        this.rabbitService.sendDelayMsg(MqConst.EXCHANGE_DIRECT_ORDER_CANCEL,MqConst.ROUTING_ORDER_CANCEL,orderId,MqConst.DELAY_TIME);
        return orderId;
    }

    @Override
    public String getTradeNo(String userId) {
        //  声明一个TradeNo
        String tradeNo = UUID.randomUUID().toString();
        //  直接将这个流水号保存到redis
        String tradeNoKey = "tradeNo:"+userId;
        this.redisTemplate.opsForValue().set(tradeNoKey,tradeNo);
        //  返回流水号
        return tradeNo;
    }

    /**
     *
     * @param tradeNo 页面传递的
     * @param userId 获取缓存流水号
     * @return
     */
    @Override
    public Boolean checkTradeNo(String tradeNo, String userId) {
        //  获取到key
        String tradeNoKey = "tradeNo:"+userId;
        //  获取流水号
        String redisTradeNo = (String) this.redisTemplate.opsForValue().get(tradeNoKey);
        //  比较并返回结果.
        return tradeNo.equals(redisTradeNo);
    }

    @Override
    public void delTradeNo(String userId) {
        //  获取到key
        String tradeNoKey = "tradeNo:"+userId;
        this.redisTemplate.delete(tradeNoKey);
    }

    @Override
    public Boolean checkStock(Long skuId, Integer skuNum) {
        //  http://localhost:9001
        //  this.wareUrl = http://localhost:9001
        //  http://localhost:9001/hasStock?skuId=10221&num=2
        String result = HttpClientUtil.doGet(wareUrl + "/hasStock?skuId=" + skuId + "&num=" + skuNum);
        //  0：无库存   1：有库存
        return "1".equals(result);
    }

    @Override
    public IPage<OrderInfo> getOrderList(Page<OrderInfo> orderInfoPage, String userId, String orderStatus) {
        //  调用mapper
        IPage<OrderInfo> iPage = orderInfoMapper.selectOrderList(orderInfoPage,userId,orderStatus);
        //  获取集合
        iPage.getRecords().stream().forEach(orderInfo -> {
            //  获取订单状态名称.
            String statusName = OrderStatus.getStatusNameByStatus(orderInfo.getOrderStatus());
            orderInfo.setOrderStatusName(statusName);
        });
        return iPage;
    }

    @Override
    public void execExpiredOrder(Long orderId) {
        //  状态: CLOSED
        //  update order_info set order_status = 'CLOSED' , process_status = 'CLOSED' WHERE id = ?;
        //        OrderInfo orderInfo = new OrderInfo();
        //        orderInfo.setId(orderId);
        //        orderInfo.setOrderStatus(OrderStatus.CLOSED.name());
        //        orderInfo.setProcessStatus(ProcessStatus.CLOSED.name());
        //        orderInfoMapper.updateById(orderInfo);

        //  后续业务处理上，还会根据订单Id 修改订单状态变为PAID.
        //        OrderInfo orderInfo = new OrderInfo();
        //        orderInfo.setId(orderId);
        //        orderInfo.setOrderStatus(OrderStatus.PAID.name());
        //        orderInfo.setProcessStatus(ProcessStatus.PAID.name());
        //        orderInfoMapper.updateById(orderInfo);
        //        this.updateOrderStatus(orderId,ProcessStatus.PAID);
        this.updateOrderStatus(orderId,ProcessStatus.CLOSED);

        //  发送一个消息异步关闭交易记录信息.
        this.rabbitService.sendMsg(MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE,MqConst.ROUTING_PAYMENT_CLOSE,orderId);
    }

    @Override
    public OrderInfo getOrderInfo(Long orderId) {
        //  获取到订单对象
        OrderInfo orderInfo = this.orderInfoMapper.selectById(orderId);
        if (orderInfo!=null){
            //  查询订单明细.
            LambdaQueryWrapper<OrderDetail> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(OrderDetail::getOrderId,orderId);
            List<OrderDetail> orderDetails = this.orderDetailMapper.selectList(lambdaQueryWrapper);
            orderInfo.setOrderDetailList(orderDetails);
        }
        //  返回数据.
        return orderInfo;
    }

    /**
     * 根据订单Id 更新订单状态.
     * @param orderId
     * @param processStatus
     */
    public void updateOrderStatus(Long orderId, ProcessStatus processStatus) {
        //  声明订单对象
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setId(orderId);
        orderInfo.setOrderStatus(processStatus.getOrderStatus().name());
        orderInfo.setProcessStatus(processStatus.name());
        //  调用更新方法.
        orderInfoMapper.updateById(orderInfo);
    }

    @Override
    public void sendOrderStatus(Long orderId) {
        //  修改订单进度状态.
        this.updateOrderStatus(orderId,ProcessStatus.NOTIFIED_WARE);
        //  要够成Json 字符串. orderInfo + orderDetail
        OrderInfo orderInfo = this.getOrderInfo(orderId);
        //  将orderInfo 与 orderDetail 中的部分字段封装到map中.
        Map map = this.initWare(orderInfo);
        //  将这个map 变为json 发送给库存系统.
        this.rabbitService.sendMsg(MqConst.EXCHANGE_DIRECT_WARE_STOCK,MqConst.ROUTING_WARE_STOCK, JSON.toJSONString(map));
    }

    @Override
    public Map initWare(OrderInfo orderInfo) {
        //  声明一个Map 集合
        HashMap<String, Object> map = new HashMap<>();
        map.put("orderId",orderInfo.getId());
        map.put("consignee",orderInfo.getConsignee());
        map.put("consigneeTel",orderInfo.getConsigneeTel());
        map.put("orderComment",orderInfo.getOrderComment());
        map.put("orderBody",orderInfo.getTradeBody());
        map.put("deliveryAddress",orderInfo.getDeliveryAddress());
        map.put("paymentWay","2");
        map.put("wareId",orderInfo.getWareId());

        //  details:[{skuId:101,skuNum:1,skuName:’小米手64G’},{skuId:201,skuNum:1,skuName:’索尼耳机’}]
        //  获取到订单明细集合.
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        //  获取封装之后的数据集合.
        List<HashMap<String, Object>> mapList = orderDetailList.stream().map(orderDetail -> {
            //  声明一个Map 集合
            HashMap<String, Object> hashMap = new HashMap<>();
            hashMap.put("skuId", orderDetail.getSkuId());
            hashMap.put("skuNum", orderDetail.getSkuNum());
            hashMap.put("skuName", orderDetail.getSkuName());
            return hashMap;
        }).collect(Collectors.toList());
        //  存储的是订单明细集合
        map.put("details",mapList);
        //  返回数据.
        return map;
    }

    @Override
    public List<OrderInfo> orderSplit(Long orderId, String wareSkuMap) {
        //  声明子订单集合.
        List<OrderInfo> orderInfoList = new ArrayList<>();
        //  1.  先获取到原始订单数据.
        OrderInfo orderInfo = this.getOrderInfo(orderId);
        //  2.  wareSkuMap 指定了如何拆单：[{"wareId":"1","skuIds":["21","22"]},{"wareId":"2","skuIds":["23"]}]
        //  3.  将wareSkuMap参数转换为能操作的对象.
        List<Map> mapList = JSON.parseArray(wareSkuMap, Map.class);
        //  4.  判断这个集合，进行拆单处理.
        if (!CollectionUtils.isEmpty(mapList)){
            //  循环遍历
            for (Map map : mapList) {
                //  获取到仓库Id
                String wareId = (String) map.get("wareId");
                //  获取到仓库Id 对应的商品Id 集合
                List<String> skuIds = (List<String>) map.get("skuIds");
                //  5.  创建子订单并给子订单赋值.
                OrderInfo subOrderInfo = new OrderInfo();
                BeanUtils.copyProperties(orderInfo,subOrderInfo);
                //  主键复制的话，会导致主键冲突. 实现主键自增.
                subOrderInfo.setId(null);
                //  赋值仓库Id
                subOrderInfo.setWareId(wareId);
                //  赋值父订单Id
                subOrderInfo.setParentOrderId(orderId);
                //  计算子订单的价格 ---> 找到子订单明细集合. 原始订单明细中包含了所有的订单明细 21,22,23 ; 新的子订单明细在哪? "skuIds":["21","22"]
                //  找到 skuId 相同的数据，就是子订单明细!
                //  获取到原始的订单明细。
                //  redis mq es;
                //  boolean test(T t);
                List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
                List<OrderDetail> orderDetailList1 = orderDetailList.stream().filter(orderDetail -> {
                    return skuIds.contains(orderDetail.getSkuId().toString());
                }).collect(Collectors.toList());
                //  将子订单明细赋值给子订单
                subOrderInfo.setOrderDetailList(orderDetailList1);
                //  计算总金额.
                subOrderInfo.sumTotalAmount();
                //  将子订单保存到数据库.
                this.saveOrderInfo(subOrderInfo);
                orderInfoList.add(subOrderInfo);
            }
        }
        //  修改原始订单状态.
        this.updateOrderStatus(orderId,ProcessStatus.SPLIT);
        //  返回子订单集合.
        return orderInfoList;
    }

    @Override
    public void execExpiredOrder(Long orderId, String flag) {
        //  修改订单状态.
        this.updateOrderStatus(orderId,ProcessStatus.CLOSED);
        if ("2".equals(flag)){
            //  发送一个消息异步关闭交易记录信息.
            this.rabbitService.sendMsg(MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE,MqConst.ROUTING_PAYMENT_CLOSE,orderId);
        }

    }
}
